package com.xmy.study.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xmy.study.entity.EduShutnumVideo;
import com.xmy.study.entity.EduVideo;
import com.xmy.study.service.IEduShutnumVideoService;
import com.xmy.study.service.IEduVideoService;
import com.xmy.study.util.Code;
import com.xmy.study.util.Result;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.MultimediaInfo;

import java.io.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static java.lang.String.valueOf;

/**
 * <文件上传>
 * 文件上传 前端控制器
 * </文件上传>
 *
 * @author HeLin
 * @since 2023-07-10
 */
@Slf4j
@RestController
@RequestMapping("/api/admin/uploading")
public class FileUpController {

    //上传文件保存的本地目录，使用@Value获取全局配置文件中配置的属性值，如 E:/wmx/uploadFiles/
    @Value("${uploadFile.location}")
    private String uploadFileLocation;

    @Value("${uploadFile.chunkNum: 5}")
    private int chunkNum;

    @Value("${uploadFile.parallel: 4}")
    private int parallel;

    @Autowired
    IEduVideoService eduVideoService;
    @Autowired
    IEduShutnumVideoService eduShutnumVideoService;

    private static final int CHUNK_SIZE = 1024 * 1024; // 每个片段的大小为 1MB

    //为了后来系统的兼容性，应该所有对象都是以json的方式进行保存
    @Autowired //如果给redis中保存数据会使用默认的序列化机制，导致redis中保存的对象不可视
            RedisTemplate<Object, Object> redisTemplate;

    @PostMapping("videoFile")
    @ApiOperation("文件上传-切片")
    public Result videoFile(@RequestParam("file") MultipartFile file, @RequestParam Map<String, String> reMap) {
        //切片上传视频
        uploadVideo(file, reMap);
        return new Result(Code.OK, "", Code.OK_MSG);
    }
    @PostMapping("uploadTransferTo")
    @ApiOperation("文件上传-零拷贝")
    public Result uploadTransferTo(@RequestParam("file") MultipartFile file, @RequestParam Map<String, String> reMap) {
        //零拷贝上传视频
        String s = synchronyUploadTransferTo(file, reMap);
        return new Result(Code.OK, s, Code.OK_MSG);
    }
    protected Map<String, String> fileUpData(MultipartFile file, String filePath) {
        //文件夹路径：
        String uploadUrl = uploadFileLocation+filePath;
        File fileUp = new File(uploadUrl);
        // 若不存在则创建该文件夹
        if (!fileUp.exists()) {
            fileUp.mkdirs();
        }

        //获取上传文件的后缀名
        String fileType = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().indexOf("."));
        String fileName;
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        Map<String, String> map = new HashMap<>();
        try {
            fileName = uuid + fileType;
            file.transferTo(new File(uploadUrl, fileName));
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
            map.put("code", valueOf(Code.ERROR));
            return map;
        }
        String fileLink = filePath+fileName;
        map.put("code", valueOf(Code.OK));
        map.put("fileType", fileType);
        map.put("fileName", fileName);
        map.put("fileLink", fileLink);
        map.put("filePath", uploadUrl);
        return map;
    }

    private String calculateMD5(MultipartFile videoFile) {
        try {
            // 获取文件的输入流
            InputStream inputStream = videoFile.getInputStream();
            // 计算文件的MD5值
            return DigestUtils.md5Hex(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Async("uploadVideo")
    protected String synchronyUploadTransferTo(MultipartFile file, @RequestParam Map<String, String> reMap) {
        EduVideo eduVideo = new EduVideo();
        //处理传值
        String name = "";
        String sort = "";
        String teacherId = "";
        String teacherName = "";
        String fileMins = "";
        if (reMap.get("name") != null) {
            name = reMap.get("name");
        }
        if (reMap.get("sort") != null) {
            sort = reMap.get("sort");
        }
        if (reMap.get("teacherId") != null) {
            teacherId = reMap.get("teacherId");
        }
        if (reMap.get("teacherName") != null) {
            teacherName = reMap.get("teacherName");
        }
        if (reMap.get("fileMins") != null) {
            fileMins = reMap.get("fileMins");
        }
        //处理md5效验
        String isToMd5 = calculateMD5(file);
        QueryWrapper<EduVideo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("file_identifier", isToMd5);
        queryWrapper.last("LIMIT 0,1");
        List<EduVideo> listVideo = eduVideoService.list(queryWrapper);
        String imgPath = "";
        if (!listVideo.isEmpty()) {
            imgPath = listVideo.get(0).getFilePath();
            eduVideo = listVideo.get(0);
            eduVideo.setId(null);
        }
        eduVideo.setFileIdentifier(isToMd5);
        if (!Objects.equals(name, "")) {
            eduVideo.setName(name);
        }
        if (!Objects.equals(sort, "")) {
            //判断是否为数字
            Pattern pattern = Pattern.compile("^[-+]?\\d*$");
            boolean sortB = pattern.matcher(sort).matches();
            if (!sortB) {
                return "请正确输入排序号，例如：1";//map;//new Result(Code.SAVE_ERROR, Code.SAVE_ERROR_MSG, "请正确输入排序号，例如：1");
            }
            eduVideo.setSort(Integer.valueOf(sort));
        }
        if (!Objects.equals(teacherId, "")) {
            eduVideo.setTeacherId(Long.valueOf(teacherId));
        }
        if (!Objects.equals(teacherName, "")) {
            eduVideo.setTeacherName(teacherName);
        }
        if (!Objects.equals(fileMins, "")) {
            eduVideo.setFileMins(Integer.valueOf(fileMins));
        }
        // 获取文件名和大小信息
        String fileName = file.getOriginalFilename();
        if (Objects.equals(name, "")) {
            eduVideo.setName(fileName);
        }
        long fileSize = file.getSize();
        eduVideo.setFileSize(fileSize);
        String courseId = reMap.get("courseId");
        String shutnumId = reMap.get("shutnumId");
        String vedioId = reMap.get("vedioId");
        //拿编辑数据
        EduShutnumVideo sVideoRs = new EduShutnumVideo();
        if (reMap.get("courseId") != null && reMap.get("shutnumId") != null && reMap.get("vedioId") != null) {
            QueryWrapper<EduShutnumVideo> queryWrapperCount = new QueryWrapper<>();
            queryWrapperCount.eq("course_id", courseId);
            queryWrapperCount.eq("shutnum_id", shutnumId);
            queryWrapperCount.eq("vedio_id", vedioId);
            long count = eduShutnumVideoService.count(queryWrapperCount);
            if (count > 0L) {
                eduVideo.setId(Long.valueOf(vedioId));
                sVideoRs = eduShutnumVideoService.getOne(queryWrapperCount);
            }
        }
        EduShutnumVideo eduShutnumVideo = new EduShutnumVideo();
        eduShutnumVideo.setCourseId(Long.valueOf(courseId));
        eduShutnumVideo.setShutnumId(Long.valueOf(shutnumId));
        //key
        //日期目录
        LocalDate today = LocalDate.now();
        if (!listVideo.isEmpty() && (reMap.get("vedioId") == null || reMap.get("vedioId").isEmpty())) {
            eduVideoService.save(eduVideo);
            //修改关系表
            eduShutnumVideo.setVedioId(eduVideo.getId());
            if (sVideoRs != null && sVideoRs.getId() != null) {
                eduShutnumVideo.setId(sVideoRs.getId());
                eduShutnumVideoService.updateById(eduShutnumVideo);
            } else {
                //写入关系表
                eduShutnumVideoService.save(eduShutnumVideo);
            }
            System.out.println("已存在，不用重复上传：" + imgPath);
            return "已存在，不用重复上传：" + imgPath;
        } else {
            String chunkNumFileIsToMd5 = "chunkNumFileIsToMd5"+isToMd5;
            if (isToMd5 != null) {
                Object getChunkNumFileIsToMd5 = redisTemplate.opsForValue().get(chunkNumFileIsToMd5);
                if (getChunkNumFileIsToMd5 == null) {
                    redisTemplate.opsForValue().set(chunkNumFileIsToMd5, isToMd5, 1, TimeUnit.HOURS);
                } else {
                    if (getChunkNumFileIsToMd5.equals(isToMd5)) {
                        System.out.println("redis判断重复上传：" + isToMd5);
                        return "redis判断重复上传：" + isToMd5;
                    }
                }
            }
        }
        //获取上传文件的后缀名
        int dotIndex = 0;
        if (fileName != null) {
            dotIndex = fileName.lastIndexOf('.');
        }
        String fileType = null;
        if (fileName != null) {
            fileType = "."+fileName.substring(dotIndex + 1);
        }

        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        String fileNameKey = "/chunk/" + today + "/" + isToMd5 + fileType;
        eduVideo.setObjectKey(fileNameKey);
        String newName = "/" + uuid + fileType;
        String videoMl = "/mathematics/video/" + today;
        if (Objects.equals(imgPath, "")) {
            eduVideo.setFilePath(videoMl + newName);
        } else {
            eduVideo.setFilePath(imgPath);
        }
        try {
            //文件流
            EduShutnumVideo finalSVideoRs = sVideoRs;
            EduVideo finalEduVideo = eduVideo;
            Flux.just(1)
                .parallel(parallel)//: 将数据流的处理并行化，使用8个线程进行处理。这样可以提高数据处理的效率。
                .runOn(Schedulers.newParallel("chunkNumFile"))//: 指定数据流的处理在名为"yy"的并行调度器上执行。这允许你自定义调度器的
                .subscribe(u -> {
                    // 文件名
                    String newFileName = uploadFileLocation + videoMl + newName;
                    // 创建目标文件
                    File dest = new File(newFileName);
                    // 向指定路径写入文件
                    try {
                        // 创建文件保存目录
                        File uploadDirAdd = new File(uploadFileLocation + videoMl);
                        if (!uploadDirAdd.exists()) {
                            uploadDirAdd.mkdirs();
                        }
                        file.transferTo(dest);
                        if (finalEduVideo.getId() == null) {
                            QueryWrapper<EduVideo> eduVideoQueryWrapper = new QueryWrapper<>();
                            eduVideoQueryWrapper.eq("file_identifier", isToMd5);
                            long count = eduVideoService.count(eduVideoQueryWrapper);
                            if (count == 0L) {
                                //视频时长
                                long duration = getDurationBackMillis2(newFileName);
                                int jc = (int) (duration / 1000);
                                finalEduVideo.setFileMins(jc);
                                //输入mysql数据库
                                eduVideoService.save(finalEduVideo);
                                //写入关系表
                                if (finalSVideoRs.getId() == null) {
                                    eduShutnumVideo.setVedioId(finalEduVideo.getId());
                                    eduShutnumVideoService.save(eduShutnumVideo);
                                }
                            }
                        } else {
                            EduVideo eduVideoArr = listVideo.get(0);
                            finalEduVideo.setFileIdentifier(eduVideoArr.getFileIdentifier());
                            finalEduVideo.setFileSize(eduVideoArr.getFileSize());
                            finalEduVideo.setFileMins(eduVideoArr.getFileMins());
                            finalEduVideo.setFileIdentifier(eduVideoArr.getFileIdentifier());
                            eduVideoService.updateById(finalEduVideo);
                            //修改关系表
                            if (finalSVideoRs != null && finalSVideoRs.getId() != null) {
                                eduShutnumVideo.setVedioId(finalEduVideo.getId());
                                eduShutnumVideo.setId(finalSVideoRs.getId());
                                eduShutnumVideoService.updateById(eduShutnumVideo);
                            }
                        }
                    } catch (IOException e) {
//                        throw new RuntimeException(e);
                    }
                });
            return "上传成功！" + uploadFileLocation + videoMl + newName;
        } catch (Exception e) {
//            e.printStackTrace();
            return "文件上传失败" + e.getMessage();
        }
    }

    @Async("uploadVideo")
    public void uploadVideo(MultipartFile file, @RequestParam Map<String, String> reMap) {
//        Map<String, Object> map = new HashMap<>();
        try {
            EduVideo eduVideo = new EduVideo();
            //处理传值
            String name = "";
            String sort = "";
            String teacherId = "";
            String teacherName = "";
            String fileMins = "";
            if (reMap.get("name") != null) {
                name = reMap.get("name");
            }
            if (reMap.get("sort") != null) {
                sort = reMap.get("sort");
            }
            if (reMap.get("teacherId") != null) {
                teacherId = reMap.get("teacherId");
            }
            if (reMap.get("teacherName") != null) {
                teacherName = reMap.get("teacherName");
            }
            if (reMap.get("fileMins") != null) {
                fileMins = reMap.get("fileMins");
            }
            //处理md5效验
            String isToMd5 = calculateMD5(file);
            QueryWrapper<EduVideo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("file_identifier", isToMd5);
            queryWrapper.last("LIMIT 0,1");
            List<EduVideo> listVideo = eduVideoService.list(queryWrapper);
            String imgPath = "";
            if (!listVideo.isEmpty()) {
                imgPath = listVideo.get(0).getFilePath();
                eduVideo = listVideo.get(0);
                eduVideo.setId(null);
            }
            eduVideo.setFileIdentifier(isToMd5);
            if (!Objects.equals(name, "")) {
                eduVideo.setName(name);
            }
            if (!Objects.equals(sort, "")) {
                //判断是否为数字
                Pattern pattern = Pattern.compile("^[-+]?\\d*$");
                boolean sortB = pattern.matcher(sort).matches();
                if (!sortB) {
//                    return map;//new Result(Code.SAVE_ERROR, Code.SAVE_ERROR_MSG, "请正确输入排序号，例如：1");
                }
                eduVideo.setSort(Integer.valueOf(sort));
            }
            if (!Objects.equals(teacherId, "")) {
                eduVideo.setTeacherId(Long.valueOf(teacherId));
            }
            if (!Objects.equals(teacherName, "")) {
                eduVideo.setTeacherName(teacherName);
            }
            if (!Objects.equals(fileMins, "")) {
                eduVideo.setFileMins(Integer.valueOf(fileMins));
            }
            // 获取文件名和大小信息
            String fileName = file.getOriginalFilename();
            if (Objects.equals(name, "")) {
                eduVideo.setName(fileName);
            }
            long fileSize = file.getSize();
            eduVideo.setFileSize(fileSize);
            // 计算需要分割的片段数量
            int chunkCount = (int) Math.ceil((double) fileSize / CHUNK_SIZE);
            eduVideo.setChunkNum(chunkCount);
            String courseId = reMap.get("courseId");
            String shutnumId = reMap.get("shutnumId");
            String vedioId = reMap.get("vedioId");
            //拿编辑数据
            EduShutnumVideo sVideoRs = new EduShutnumVideo();
            if (reMap.get("courseId") != null && reMap.get("shutnumId") != null && reMap.get("vedioId") != null) {
                QueryWrapper<EduShutnumVideo> queryWrapperCount = new QueryWrapper<>();
                queryWrapperCount.eq("course_id", courseId);
                queryWrapperCount.eq("shutnum_id", shutnumId);
                queryWrapperCount.eq("vedio_id", vedioId);
                long count = eduShutnumVideoService.count(queryWrapperCount);
                if (count > 0L) {
                    eduVideo.setId(Long.valueOf(vedioId));
                    sVideoRs = eduShutnumVideoService.getOne(queryWrapperCount);
                }
            }
            EduShutnumVideo eduShutnumVideo = new EduShutnumVideo();
            eduShutnumVideo.setCourseId(Long.valueOf(courseId));
            eduShutnumVideo.setShutnumId(Long.valueOf(shutnumId));
            //key
            //日期目录
            LocalDate today = LocalDate.now();
            if (!listVideo.isEmpty() && (reMap.get("vedioId") == null || reMap.get("vedioId").isEmpty())) {
                eduVideoService.save(eduVideo);
                //修改关系表
                eduShutnumVideo.setVedioId(eduVideo.getId());
                if (sVideoRs != null && sVideoRs.getId() != null) {
                    eduShutnumVideo.setId(sVideoRs.getId());
                    eduShutnumVideoService.updateById(eduShutnumVideo);
                } else {
                    //写入关系表
                    eduShutnumVideoService.save(eduShutnumVideo);
                }
                System.out.println("已存在，不用重复上传：" + imgPath);
                return;
            } else {
                String chunkNumFileIsToMd5 = "chunkNumFileIsToMd5"+isToMd5;
                if (isToMd5 != null) {
                    Object getChunkNumFileIsToMd5 = redisTemplate.opsForValue().get(chunkNumFileIsToMd5);
                    if (getChunkNumFileIsToMd5 == null) {
                        redisTemplate.opsForValue().set(chunkNumFileIsToMd5, isToMd5, 1, TimeUnit.HOURS);
                    } else {
                        if (getChunkNumFileIsToMd5.equals(isToMd5)) {
                            System.out.println("redis判断重复上传：" + isToMd5);
                            return;
                        }
                    }
                }
            }
            //获取上传文件的后缀名
//            String fileType = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().indexOf("."));
            int dotIndex = 0;
            if (fileName != null) {
                dotIndex = fileName.lastIndexOf('.');
            }
            String fileType = null;
            if (fileName != null) {
                fileType = "."+fileName.substring(dotIndex + 1);
            }

            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            String fileNameKey = "/chunk/" + today + "/" + isToMd5 + fileType;
            eduVideo.setObjectKey(fileNameKey);
            String newName = "/" + uuid + fileType;
            String videoMl = "/mathematics/video/" + today;
            if (Objects.equals(imgPath, "")) {
                eduVideo.setFilePath(videoMl + newName);
            } else {
                eduVideo.setFilePath(imgPath);
            }
//            //视频时长
//            long durationIf = getDurationBackMillis2(file.getOriginalFilename());
//            if (durationIf < 1L) {
//                System.out.println("当前视频格式不支持"+file.getOriginalFilename());
//                return;//当前视频格式不支持
//            }
            //每个分片大小
            eduVideo.setChunkSize((long) CHUNK_SIZE);
            // 创建临时文件保存目录
            File uploadDir = new File(uploadFileLocation + fileNameKey);
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }
            //        return new Result(Code.OK, "", isToMd5);

            // 记录每个片段的索引和大小信息
            Map<Integer, Long> chunkIndexes = new LinkedHashMap<>();
            //进度条 chunkCount
            //设置redis
            String redisKey = "";
            if (reMap.get("userId") != null) {
                redisKey = "chunkCount" + reMap.get("userId");
                String hkKey = courseId+shutnumId;
                Object o = redisTemplate.opsForHash().get(redisKey, hkKey);
                ArrayList<Object> getRedisArr = new ArrayList<>();
                int isGet = 0;
                if (o != null) {
                    getRedisArr = (ArrayList<Object>) o;
                    for (Object o1: getRedisArr) {
                        HashMap<Object, Object> redisMapGet = (HashMap<Object, Object>) o1;
                        if (Objects.equals(isToMd5, redisMapGet.get("md5").toString())) {
                            isGet = 1;
                        }
                    }
                }
                if (isGet == 0) {
                    HashMap<Object, Object> redisMap = new HashMap<>();
                    redisMap.put("md5", isToMd5);
                    redisMap.put("chunkCount", chunkCount);
                    redisMap.put("fileNameKey", uploadFileLocation + fileNameKey);
                    redisMap.put("name", eduVideo.getName());
                    redisMap.put("value", 0);
                    getRedisArr.add(redisMap);
                    //写入 redis 进度条
                    redisTemplate.opsForHash().put(redisKey, hkKey, getRedisArr);
                }
            }
            //同时跑多个切片
            EduShutnumVideo finalSVideoRs = sVideoRs;
            EduVideo finalEduVideo = eduVideo;
            Flux.range(1, chunkCount)
                    //限流触发，看上游是怎么限流获取数据的
                    .limitRate(chunkNum) //一次预取100个元素； 第一次 request(100)，以后request(75)，75%，后面卡的话在对应进行限流
//                    .buffer(100)//: 将数据流中的数据分成大小为100的缓冲区。这意味着每个缓冲区包含100个连续的元素。这里因为要用到切片，所以不用分组。
                    .parallel(parallel)//: 将数据流的处理并行化，使用8个线程进行处理。这样可以提高数据处理的效率。
                    .runOn(Schedulers.newParallel("chunkNumFile"))//: 指定数据流的处理在名为"yy"的并行调度器上执行。这允许你自定义调度器的
                    .subscribe(u -> {
                        int i = u - 1;
                        // 计算当前片段的索引和大小
                        int index = i;
                        //断点续传 处理
                        List<File> files = Arrays.stream(new File(uploadFileLocation + fileNameKey).listFiles())
                                .filter(f -> f.getName().startsWith("part-" + index))
                                .collect(Collectors.toList());
                        if (files.isEmpty()) {
                            long start = i * CHUNK_SIZE;
                            long end = Math.min((i + 1) * CHUNK_SIZE - 1, fileSize - 1);

                            // 将当前片段保存到临时文件中
                            File chunkFile = new File(uploadDir, "part-" + index);//fileName.substring(0, fileName.lastIndexOf('.')) +
                            try {
                                chunkFile.createNewFile();
                                try (FileOutputStream fos = new FileOutputStream(chunkFile)) {
                                    fos.write(file.getBytes(), (int) start, (int) (end - start + 1));
                                }
                                // 记录当前片段的索引和大小信息
                                chunkIndexes.put(index, end);
                                //            System.out.println("index：" + index + " end:" +end);
                            } catch (IOException e) {
//                                throw new RuntimeException(e);
                            }
                        }
                        if (u == chunkCount) {
                            //        eduVideoService.save(eduVideo);
                            if (finalEduVideo.getId() == null) {
                                //            System.out.println("eduVideo：" + eduVideo + " bytes");
                                QueryWrapper<EduVideo> eduVideoQueryWrapper = new QueryWrapper<>();
                                eduVideoQueryWrapper.eq("file_identifier", isToMd5);
                                long count = eduVideoService.count(eduVideoQueryWrapper);
                                if (count == 0L) {
                                    //切片组合视频
                                    boolean b = mergeVideoSlices(videoMl, fileNameKey, chunkCount, newName);
                                    if (b) {
                                        //视频时长
                                        long duration = getDurationBackMillis2(uploadFileLocation + videoMl + newName);
                                        int jc = (int) (duration / 1000);
                                        finalEduVideo.setFileMins(jc);
                                    }
                                    //输入mysql数据库
                                    eduVideoService.save(finalEduVideo);
                                    //写入关系表
                                    if (finalSVideoRs.getId() == null) {
                                        eduShutnumVideo.setVedioId(finalEduVideo.getId());
                                        eduShutnumVideoService.save(eduShutnumVideo);
                                    }
                                }
                            } else {
                                EduVideo eduVideoArr = listVideo.get(0);
                                finalEduVideo.setFileIdentifier(eduVideoArr.getFileIdentifier());
                                finalEduVideo.setFileSize(eduVideoArr.getFileSize());
                                finalEduVideo.setFileMins(eduVideoArr.getFileMins());
                                finalEduVideo.setFileIdentifier(eduVideoArr.getFileIdentifier());
                                eduVideoService.updateById(finalEduVideo);
                                //修改关系表
                                if (finalSVideoRs != null && finalSVideoRs.getId() != null) {
                                    eduShutnumVideo.setVedioId(finalEduVideo.getId());
                                    eduShutnumVideo.setId(finalSVideoRs.getId());
                                    eduShutnumVideoService.updateById(eduShutnumVideo);
                                }
                            }
//                            map.put("eduVideo", finalEduVideo);
//                            map.put("eduShutnumVideo", eduShutnumVideo);//sVideoRs
//            return map;//new Result(Code.OK, map, Code.OK_MSG);
                            // 返回上传结果
                            //        return ResponseEntity.ok("Upload success!");
                        }
                    });
//            for (int i = 0; i < chunkCount; i++) {
//                // 计算当前片段的索引和大小
//                int index = i;
//                //断点续传 处理
//                List<File> files = Arrays.stream(new File(uploadFileLocation + fileNameKey).listFiles())
//                        .filter(f -> f.getName().startsWith("part-" + index))
//                        .collect(Collectors.toList());
//                if (!files.isEmpty()) {
//                    continue;
//                }
//                long start = i * CHUNK_SIZE;
//                long end = Math.min((i + 1) * CHUNK_SIZE - 1, fileSize - 1);
//
//                // 将当前片段保存到临时文件中
//                File chunkFile = new File(uploadDir, "part-" + index);//fileName.substring(0, fileName.lastIndexOf('.')) +
//                chunkFile.createNewFile();
//                try (FileOutputStream fos = new FileOutputStream(chunkFile)) {
//                    fos.write(file.getBytes(), (int) start, (int) (end - start + 1));
//                }
//
//                // 记录当前片段的索引和大小信息
//                chunkIndexes.put(index, end);
//                //            System.out.println("index：" + index + " end:" +end);
//            }
            //        System.out.println("chunkIndexes：" + chunkIndexes);
        } catch (NumberFormatException e) {
//            throw new RuntimeException(e);
//            return map;
        }
//        catch (IOException e) {
////            throw new RuntimeException(e);
////            return map;
//        }
    }

    //上传进度条
    @GetMapping("progressBar")
    public Result progressBar(@RequestParam Map<String, String> reMap) {
        String redisKey = "";
        ArrayList<Object> getRedisArr = new ArrayList<>();
        String courseId = reMap.get("courseId");
        String shutnumId = reMap.get("shutnumId");
        String hkKey = courseId + shutnumId;
        if (reMap.get("userId") != null) {
            redisKey = "chunkCount" + reMap.get("userId");
            Object o = redisTemplate.opsForHash().get(redisKey, hkKey);
            if (o != null) {
                getRedisArr = (ArrayList<Object>) o;
            }
        }
        Map<String, Object> map = new HashMap<>();
        ArrayList<Object> getRedisArrAdd = new ArrayList<>();
        if (!Objects.equals(redisKey, "")) {
            if (!getRedisArr.isEmpty()) {
//                Object md5 = redisTemplate.opsForHash().get(redisKey, "md5");
                for (Object getArr: getRedisArr) {
                    HashMap<Object, Object> redisMap = (HashMap<Object, Object>) getArr;
                    Object chunkCount = redisMap.get("chunkCount");
                    Object fileNameKey = redisMap.get("fileNameKey");
                    if (fileNameKey == null || Objects.equals(fileNameKey.toString(), "")) {
                        continue;
                    }
                    for (int i = Integer.parseInt(chunkCount.toString()); i>0; i--) {
                        int index = i;
                        //断点续传 处理
                        List<File> files = Arrays.stream(new File(fileNameKey.toString()).listFiles())
                                .filter(f -> f.getName().startsWith("part-" + index))
                                .collect(Collectors.toList());
                        if (!files.isEmpty()) {
                            double cs = index + 1;
                            double bcs = Double.parseDouble(chunkCount.toString());
                            double value = cs / bcs * 100;
//                        System.out.println(files+"至少需要两个切片才能合并"+index+"value:"+value+"chunkCount:"+chunkCount+"cs:"+cs+"bcs:"+bcs);
                            BigDecimal two1 = new BigDecimal(value);
                            double three1 = two1.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                            if (three1 == 100) {
                                break;
                            }
                            int isGet = 0;
                            for (Object o: getRedisArrAdd) {
                                HashMap<Object, Object> redisMapGet = (HashMap<Object, Object>) o;
                                if (Objects.equals(redisMap.get("md5").toString(), redisMapGet.get("md5").toString())) {
                                    isGet = 1;
                                }
                            }
                            if (isGet == 0) {
                                //传完了，删除进度条
                                HashMap<Object, Object> redisMapAdd = new HashMap<>();
                                redisMapAdd.put("md5", redisMap.get("md5"));
                                redisMapAdd.put("chunkCount", redisMap.get("chunkCount"));
                                redisMapAdd.put("fileNameKey", redisMap.get("fileNameKey"));
                                redisMapAdd.put("name", redisMap.get("name"));
                                redisMapAdd.put("value", three1);
                                getRedisArrAdd.add(redisMapAdd);
                            }
                        }
                    }
                }
                if (!getRedisArrAdd.isEmpty()) {
                    //写入 redis 进度条
                    redisTemplate.opsForHash().put(redisKey, hkKey, getRedisArrAdd);
                    return new Result(Code.OK, getRedisArrAdd, Code.OK_MSG);
                }
            }
        }
        map.put("value",0);
        getRedisArrAdd.add(map);
        return new Result(Code.ERROR, getRedisArrAdd, "加载中，或暂无上传内容");
    }
    //切片组合视频
    private boolean mergeVideoSlices(String name, String key, int index, String newName) {
        String uploadDir = uploadFileLocation;
        try {
            List<File> chunkFiles = new ArrayList<>();
            for (int i = 0;i<index;i++) {
                int finalI = i;
                List<File> files = Arrays.stream(new File(uploadDir + key).listFiles())
                        .filter(f -> f.getName().startsWith("part-" + finalI))
                        .collect(Collectors.toList());
                chunkFiles.add(files.get(0));
            }
            // 检查文件数量是否大于等于2（至少需要两个切片才能合并）
            if (chunkFiles.size() < 2) {
                System.out.println("至少需要两个切片才能合并");
                return false;
            }
            // 创建文件保存目录
            File uploadDirAdd = new File(uploadDir + name);
            if (!uploadDirAdd.exists()) {
                uploadDirAdd.mkdirs();
            }
            // 创建一个新的视频文件输出流
            File outputFile = new File(uploadDir + name + newName);
            FileOutputStream fos = new FileOutputStream(outputFile);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            // 遍历文件列表，将每个切片写入输出流
            for (int i = 0; i < chunkFiles.size(); i++) {
                File inputFile = chunkFiles.get(i);
                FileInputStream fis = new FileInputStream(inputFile);
                BufferedInputStream bis = new BufferedInputStream(fis);
                // 读取输入文件的内容并写入输出流
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = bis.read(buffer)) != -1) {
                    bos.write(buffer, 0, bytesRead);
                }
                // 关闭输入输出流
                bis.close();
                fis.close();
                bos.flush();
            }
            // 关闭输出流
            bos.close();
            fos.close();
            System.out.println("视频切片合并成功，输出文件：" + outputFile.getAbsolutePath());
            return true;
        } catch (IOException e) {
            System.out.println("视频切片合并失败");
//            e.printStackTrace();
            return false;
        }
    }

    private static long getDurationBackMillis2(String multipartFile){
        if(multipartFile != null){
            try{
                // 根据上传的文件名字，构建初始化的文件对象（临时文件），这个文件是空的
                File file = new File(multipartFile);

                // 将普通文件对象转换为媒体对象
                MultimediaObject multimediaObject = new MultimediaObject(file);
                // 获取媒体对象的信息对象
                MultimediaInfo info = multimediaObject.getInfo();
                // 从媒体信息对象中获取媒体的时长，单位是：ms
                long duration = info.getDuration();
                System.out.println("视频时长获取成功！"+duration);
                return duration;
            } catch(Exception e){
                System.out.println(multipartFile+"视频时长获取失败！"+e.getMessage());
                return 0L;
            }
        }
        return 0L;
    }
    @DeleteMapping("delVideo")
    @ApiOperation("删除视频信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "id", required = true, dataType = "String")
    })
    public Result delVideo(@RequestParam("id") String id) {
        EduVideo eduVideo = eduVideoService.getById(Long.valueOf(id));
        if (eduVideo == null) {
            return new Result(Code.EXP_ERROR, Code.EXP_MSG, "该视频不存在");
        }
        eduVideoService.removeById(Long.valueOf(id));
        QueryWrapper<EduShutnumVideo> eduShutnumVideoQueryWrapper = new QueryWrapper<>();
        eduShutnumVideoQueryWrapper.eq("vedio_id",id);
        eduShutnumVideoService.remove(eduShutnumVideoQueryWrapper);
        //暂时不删除 文件 和 目录
//        QueryWrapper<EduVideo> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("file_path",eduVideo.getFilePath());
//        queryWrapper.ne("id",eduVideo.getId());
//        long count = eduVideoService.count(queryWrapper);
//        if (count == 0L) {
//            // 创建要删除的文件对象
//            File file = new File(uploadFileLocation+eduVideo.getFilePath());
//            // 删除文件
//            file.delete();
//            // 创建要删除的目录对象
//            File directory = new File(uploadFileLocation+eduVideo.getObjectKey());
//            // 递归删除目录及其下的所有文件和文件夹
//            deleteDirectory(directory);
//        }
        return new Result(Code.OK, null, "视频信息删除成功~");
    }
    // 递归删除目录及其下的所有文件和文件夹
    private static void deleteDirectory(File directory) {
        // 获取目录下的所有文件和文件夹
        File[] files = directory.listFiles();
        // 遍历所有文件和文件夹，如果是文件则删除，如果是文件夹则递归删除
        for (File file : files) {
            if (file.isFile()) {
                file.delete();
            } else if (file.isDirectory()) {
                deleteDirectory(file);
            }
        }
        // 删除空目录
        directory.delete();
    }
}
